import { NextMiddleware } from 'middleware-io';
import { EventEmitter } from 'events';
import { Context, MessageContext } from 'vk-io';
import { Command } from './';
import { parseNamedRegexGroups } from './helpers';

export enum EventsTypes {
  COMMAND_NOT_FOUNDED = 'command_not_founded',
  COMMAND_EXECUTION_ERROR = 'command_execution_error',
}

/**
 * Class for commands managment
 */
export class Commander<C extends Context> {
  private readonly commands: Command<C>[] = [];
  public readonly listener = new EventEmitter();

  /**
   * Method than founds command by message text
   */
  private findCommand(text: string) {
    return this.commands.find(({ options }) => (
      text && options.triggers.regex.test(text)
    ));
  }

  private matchAndParseCommandArgs(text: string, regexTrigger: RegExp) {
    const matches = text.match(regexTrigger);

    const args = matches && matches?.groups
      ? parseNamedRegexGroups(matches.groups)
      : {}
    
    return args;
  }

  public addCommand = (command: Command<C>) => {
    this.commands.push(command);
  }

  /**
   * Middleware that founds and calls commands handlers
   */
  public middleware = async (context: C, next: NextMiddleware) => {
    if (!context.text) return next();

    const command = this.findCommand(context.text);

    if (!command) {
      return this.listener.emit(EventsTypes.COMMAND_NOT_FOUNDED, context)
    }

    const args = this.matchAndParseCommandArgs(
      context.text,
      command.options.triggers.regex,
    )

    try {
      await command.options.handler(context, args);
    } catch {
      this.listener.emit(EventsTypes.COMMAND_EXECUTION_ERROR, context, command);
    }
  }
}
